package com.codeberry.yws2;

import java.io.*;
import java.net.*;

public abstract class AbstractYoctoWebServer implements Runnable {
  private static final int DEFAULT_PORT = 8080;

  private int port = DEFAULT_PORT;
  private Thread thread;

  private volatile ServerSocket serverSocket;

  public int getPort() {
    return port;
  }

  public void setPort(int port) {
    this.port = port;
  }

  public synchronized void start() throws IOException {
    serverSocket = new ServerSocket(port);

    thread = new Thread(this, getClass().getSimpleName());
    thread.start();
  }

  public synchronized void stop() {
    if (serverSocket != null) {
      closeServerSocket();
    }
    thread = null;
  }

  public void run() {
    try {
      runServer();
    } finally {
      closeServerSocket();
    }
  }

  private void runServer() {
    while (Thread.currentThread() == thread &&
           serverSocket != null &&
           !serverSocket.isClosed()) {
      try {
        handleNextIncommingSocket();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }

  private void handleNextIncommingSocket() throws IOException {
    Socket socket = serverSocket.accept();

    new ConnectionHandler(socket).start();
  }

  private void closeServerSocket() {
    try {
      serverSocket.close();
    } catch (IOException ignore) {
    }
  }

  public abstract void process(Request request, Response response) throws IOException;

  private class ConnectionHandler extends Thread {
    private Socket socket;

    public ConnectionHandler(Socket socket) throws IOException {
      super(AbstractYoctoWebServer.this.getClass().getSimpleName() + " - " + ConnectionHandler.class.getSimpleName());
      this.socket = socket;
    }

    public void run() {
      InputStream inputStream = null;
      OutputStream outputStream = null;
      try {
        inputStream = socket.getInputStream();
        outputStream = socket.getOutputStream();

        Request request = new Request(inputStream);
        Response response = new Response(outputStream);

        process(request, response);

        outputStream.flush();
      } catch (IOException e) {
        e.printStackTrace();
      } finally {
        closeAll(inputStream, outputStream);
      }
    }

    private void closeAll(InputStream inputStream, OutputStream outputStream) {
      try {
        if (inputStream != null) {
          inputStream.close();
        }
      } catch (IOException ignore) {
      }
      try {
        if (outputStream != null) {
          outputStream.close();
        }
      } catch (IOException ignore) {
      }
      try {
        socket.close();
      } catch (IOException ignore) {
      }
    }
  }
}
